﻿using GameToolkit.UI;
using GameToolkit;
using LitJson;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Devil;

namespace GameToolkit.UI
{
    public partial class UISkinAsset
    {

        class TweenColor : UITweenState, IUIInitializer, IUIRecursiveState
        {
            public bool useAlpha;
            public Color color;

            static readonly float vnormal = 1f / 255f;
            public TweenColor() : base("color") { }
            private TweenColor(int id) : base(id) { }

            static int GetHexValue(char c)
            {
                if (c >= '0' && c <= '9')
                    return c - '0';
                if (c >= 'a' && c <= 'f')
                    return c - 'a' + 10;
                if (c >= 'A' && c <= 'F')
                    return c - 'A' + 10;
                return 0;
            }

            static float ParseValue(string rgbvalue, int index)
            {
                var value = GetHexValue(rgbvalue[index]) << 4;
                value |= GetHexValue(rgbvalue[index + 1]);
                return (float)value * vnormal;
            }

            public Object[] GetSkinComponents(GameObject target)
            {
                return target.GetComponentsInChildren<Graphic>();
            }

            public override Object GetSkinComponent(GameObject gameObject)
            {
                return gameObject.GetComponent<Graphic>();
            }

            public override UITweenState Clone(string stateName, JsonData data)
            {
                return Clone(stateName, (string)data);
            }

            public override UITweenState Clone(string statName, string rgbvalue)
            {
                var len = rgbvalue == null ? 0 : rgbvalue.Length;
                if (len != 7 && len != 9 || !pattern_color.IsMatch(rgbvalue))
                    throw new System.Exception(string.Format("\"{0}\" 不是合法的颜色值(stat:{1})", rgbvalue, statName));
                var skin = new TweenColor(id);
                skin.useAlpha = len == 9;
                Color col;
                col.r = ParseValue(rgbvalue, 1);
                col.g = ParseValue(rgbvalue, 3);
                col.b = ParseValue(rgbvalue, 5);
                if (skin.useAlpha)
                    col.a = ParseValue(rgbvalue, 7);
                else
                    col.a = 1f;
                skin.color = col;
                return skin;
            }

            public override IUITweener CreateSkin(Object target)
            {
                var graph = target as Graphic;
                if (graph == null)
                    return null;
                return new SkinColor(this, graph);
            }

            public void OnInitialize(Object target)
            {
                var graph = target as Graphic;
                if (graph == null)
                    return;
                var col = color;
                if (!useAlpha)
                    col.a = graph.color.a;
                graph.color = col;
            }
        }

        class SkinColor : IUITweener
        {
            TweenColor tween;
            Graphic graph;
            public SkinColor(TweenColor tween, Graphic target)
            {
                this.tween = tween;
                this.graph = target;
            }

            public void Activite(float duration)
            {
                if (graph != null)
                {
                    graph.CrossFadeColor(tween.color, duration, true, tween.useAlpha);
                }
            }

            public void Interrupt() { }
        }


        class TweenAlpha : UITweenState, IUIInitializer, IUIRecursiveState
        {
            float alpha;
            public TweenAlpha() : base("alpha") { }
            private TweenAlpha(int id) : base(id) { }

            public override UITweenState Clone(string stateName, JsonData data)
            {
                var st = new TweenAlpha(id);
                st.alpha = (float)data;
                return st;
            }

            public override UITweenState Clone(string stateName, string content)
            {
                var st = new TweenAlpha(id);
                Localization.TryParse(content, out st.alpha);
                return st;
            }

            public override IUITweener CreateSkin(Object target)
            {
                return new AlphaSkin(target, alpha);
            }

            public override Object GetSkinComponent(GameObject gameObject)
            {
                var grp = gameObject.GetComponent<CanvasGroup>();
                if (grp == null)
                    return gameObject.GetComponent<Graphic>();
                else
                    return grp;
            }

            public Object[] GetSkinComponents(GameObject root)
            {
                List<Object> targets = new List<Object>();
                var grp = root.GetComponentsInChildren<CanvasGroup>();
                if (grp != null)
                {
                    for (int i = 0; i < grp.Length; i++)
                    {
                        targets.Add(grp[i]);
                    }
                }
                if (targets.Count == 0)
                {
                    var gras = root.GetComponentsInChildren<Graphic>();
                    if (gras != null)
                    {
                        for (int i = 0; i < gras.Length; i++)
                        {
                            targets.Add(gras[i]);
                        }
                    }
                }
                if (targets.Count > 0)
                    return targets.ToArray();
                else
                    return null;
            }

            public void OnInitialize(Object target)
            {
                if (target is Graphic)
                {
                    var col = ((Graphic)target).color;
                    col.a = alpha;
                    ((Graphic)target).color = col;
                }
                else if (target is CanvasGroup)
                {
                    ((CanvasGroup)target).alpha = alpha;
                }
            }
        }

        class AlphaSkin : IUITweener, IYieldCommand
        {
            float alpha0;
            float alpha;
            float dt;
            CanvasGroup target;
            Graphic gtarget;
            bool breakout;
            public AlphaSkin(Object target, float alpha)
            {
                this.alpha = alpha;
                this.target = target as CanvasGroup;
                this.gtarget = target as Graphic;
            }

            public void Activite(float duration)
            {
                if (target != null)
                {
                    breakout = duration < 0.015f;
                    if (breakout)
                    {
                        target.alpha = alpha;
                    }
                    else
                    {
                        alpha0 = target.alpha;
                        dt = 1f / duration;
                        ParallelUtils.Schedule(this, true);
                        //MainThread.StartYieldCommand(this);
                    }
                }
                else if (gtarget != null)
                {
                    gtarget.CrossFadeAlpha(alpha, duration, true);
                }
            }

            public IEnumerator Execute()
            {
                float t = 0;
                yield return null;
                while (!breakout && t < 1f && target != null)
                {
                    t += Time.unscaledDeltaTime * dt;
                    target.alpha = Mathf.Lerp(alpha0, alpha, t);
                    yield return null;
                }
            }

            public void Interrupt()
            {
                breakout = true;
            }
        }

        class TweenScale : UITweenState
        {
            public Vector3 scale;

            private TweenScale(int id) : base(id) { }
            public TweenScale() : base("scale") { }

            public override UITweenState Clone(string stateName, JsonData data)
            {
                if (data == null)
                    return null;
                if (data.IsString)
                    return Clone(stateName, (string)data);
                TweenScale t = new TweenScale(id);
                t.scale = Vector3.one * (float)data;
                return t;
            }

            public override UITweenState Clone(string statName, string value)
            {
                TweenScale t = new TweenScale(id);
                if (pattern_number.IsMatch(value))
                    t.scale = Vector3.one * Localization.ParseFloat(value);
                else
                    t.scale = StringUtil.ParseVector3(value);
                return t;
            }

            public override Object GetSkinComponent(GameObject gameObject)
            {
                return gameObject.transform;
            }

            public override IUITweener CreateSkin(Object target)
            {
                return new SkinScale(target as Transform, this);
            }
        }

        class SkinScale : IUITweener, IYieldCommand
        {
            Transform transform;
            TweenScale scale;
            bool interrupted;
            float duration;
            public SkinScale(Transform trans, TweenScale scale)
            {
                this.transform = trans;
                this.scale = scale;
                interrupted = true;
            }

            public void Activite(float duration)
            {
                if (duration <= 0.01f)
                {
                    transform.localScale = scale.scale;
                    interrupted = true;
                }
                else
                {
                    interrupted = false;
                    this.duration = duration;
                    ParallelUtils.Schedule(this, true);
                    //MainThread.StartYieldCommand(this);
                }
            }

            public void Interrupt() { interrupted = true; }

            public IEnumerator Execute()
            {
                float dt = 1f / duration;
                Vector3 s0 = transform == null ? Vector3.zero : transform.localScale;
                float t = 0;
                yield return null;
                while (!interrupted && t < 1f && transform != null)
                {
                    t += Time.unscaledDeltaTime * dt;
                    transform.localScale = Vector3.Lerp(s0, scale.scale, t);
                    yield return null;
                }
            }
        }

        class TweenActive : UITweenState
        {
            public bool act;

            public TweenActive() : base("active") { }
            public TweenActive(int id) : base(id) { }

            public override Object GetSkinComponent(GameObject gameObject)
            {
                return gameObject;
            }

            public override IUITweener CreateSkin(Object target)
            {
                return new SkinActive(target as GameObject, act);
            }

            public override UITweenState Clone(string stateName, JsonData data)
            {
                var act = new TweenActive(id);
                act.act = (bool)data;
                return act;
            }

            public override UITweenState Clone(string statName, string value)
            {
                var act = new TweenActive(id);
                act.act = value.EqualsIgnoreCase("true") || value.EqualsIgnoreCase("yes");
                return act;
            }
        }

        class SkinActive : IUITweener
        {
            bool act;
            GameObject go;
            public SkinActive(GameObject go, bool act)
            {
                this.go = go;
                this.act = act;
            }

            public void Activite(float duration)
            {
                if (go != null && go.activeSelf != act)
                    go.SetActive(act);
            }

            public void Interrupt()
            {
            }
        }

        public class TweenCursor : UITweenState
        {
            public Vector2 hotspot;
            public string asset;
            public int priority;

            public TweenCursor() : base("cursor")
            {
                hotspot = new Vector2(0.5f, 0.5f);
                ParallelUtils.ScheduleLooper(CursorCfg.Loop, true);
                //MainThread.StartLooper(CursorCfg.Loop);
            }

            private TweenCursor(int id) : base(id) { }

            //public override UITweenState Clone(string stateName, XmlElement element)
            //{
            //    var stat = base.Clone(stateName, element) as TweenCursor;
            //    var hots = element.GetAttribute("hotspot");
            //    if (!string.IsNullOrEmpty(hots))
            //        stat.hotspot = StringUtil.ParseVector2(hots);
            //    return stat;
            //}

            public override UITweenState Clone(string stateName, JsonData data)
            {
                if (data == null)
                    return null;
                if (data.IsString)
                    return Clone(stateName, (string)data);
                if (data.IsObject)
                {
                    var t = new TweenCursor(id);
                    t.hotspot = StringUtil.ParseVector2((string)data["hotspot"]);
                    t.asset = (string)data["src"];
                    if (stateName == stat_highlight)
                        t.priority = 2;
                    else if (stateName == stat_selected)
                        t.priority = 1;
                    else
                        t.priority = 0;
                    return t;
                }
                return null;
            }

            public override UITweenState Clone(string stateName, string content)
            {
                var t = new TweenCursor(id);
                t.hotspot = this.hotspot;
                t.asset = content;
                if (stateName == stat_highlight)
                    t.priority = 2;
                else if (stateName == stat_selected)
                    t.priority = 1;
                else
                    t.priority = 0;
                return t;
            }

            public override IUITweener CreateSkin(Object target)
            {
                var t = new CursorCfg(this);
                t.LoadAsset();
                return t;
            }

            public override Object GetSkinComponent(GameObject gameObject)
            {
                return gameObject;
            }
        }

        public class CursorCfg : IUITweener
        {
            static bool reset;
            static CursorCfg current;
            static bool takeOver;
            public static bool DontSetCursor
            {
                get { return takeOver; }
                set
                {
                    if (takeOver != value)
                    {
                        takeOver = value;
                        reset |= !value;
                    }
                }
            }

            internal static void Loop(float deltaTime)
            {
                if (reset && !takeOver)
                {
                    reset = false;
                    if (current == null)
                    {
                        Cursor.SetCursor(null, Vector2.zero, CursorMode.Auto);
                    }
                    else if (current.tex != null)
                    {
                        Cursor.SetCursor(current.tex, current.hotspot, CursorMode.Auto);
                    }
                }
                //return true;
            }

            int priority;
            string asset;
            //TweenCursor tween;
            Texture2D tex;
            Vector2 hotspot;
            Vector2 pivot;

            public CursorCfg(int priority, string asset, Vector2 hotspot)
            {
                this.priority = priority;
                this.asset = asset;
                this.pivot = hotspot;
            }

            internal CursorCfg(TweenCursor cur)
            {
                //this.tween = cur;
                this.priority = cur.priority;
                this.asset = cur.asset;
                this.pivot = cur.hotspot;
            }

            public void LoadAsset()
            {
                if (!string.IsNullOrEmpty(asset))
                {
                    AssetsUtil.GetAssetAsync<Texture2D>(asset, (id, asset) =>
                    {
                        tex = asset;
                        hotspot = new Vector2(Mathf.Floor((tex.width - 1) * pivot.x), Mathf.Floor((tex.height - 1) * (1 - pivot.y)));
                        reset = true;
                        //if (current == this)
                        //    Cursor.SetCursor(tex, hotspot, CursorMode.Auto);
                    });
                }
            }

            public void Activite(float duration)
            {
                if (current == null || priority >= current.priority)
                {
                    reset |= this != current;
                    current = this;
                    //Cursor.SetCursor(tex, hotspot, CursorMode.Auto);
                }
            }

            public void Interrupt()
            {
                if (current == this)
                {
                    current = null;
                    reset = true;
                    //Cursor.SetCursor(null, Vector2.zero, CursorMode.Auto);
                }
            }
        }


        class TweenEffectColor : UITweenState, IUIInitializer, IUIRecursiveState
        {
            public bool useAlpha;
            public Color color;

            static readonly float vnormal = 1f / 255f;
            public TweenEffectColor() : base("effectcolor") { }
            private TweenEffectColor(int id) : base(id) { }

            static int GetHexValue(char c)
            {
                if (c >= '0' && c <= '9')
                    return c - '0';
                if (c >= 'a' && c <= 'f')
                    return c - 'a' + 10;
                if (c >= 'A' && c <= 'F')
                    return c - 'A' + 10;
                return 0;
            }

            static float ParseValue(string rgbvalue, int index)
            {
                var value = GetHexValue(rgbvalue[index]) << 4;
                value |= GetHexValue(rgbvalue[index + 1]);
                return (float)value * vnormal;
            }

            public Object[] GetSkinComponents(GameObject target)
            {
                return target.GetComponentsInChildren<Shadow>();
            }

            public override Object GetSkinComponent(GameObject gameObject)
            {
                return gameObject.GetComponent<Shadow>();
            }

            public override UITweenState Clone(string stateName, JsonData data)
            {
                return Clone(stateName, (string)data);
            }

            public override UITweenState Clone(string statName, string rgbvalue)
            {
                var skin = new TweenEffectColor(id);
                var len = rgbvalue.Length;
                if (len != 7 && len != 9 || !pattern_color.IsMatch(rgbvalue))
                    throw new System.Exception(string.Format("\"{0}\" 不是合法的颜色值", rgbvalue));
                skin.useAlpha = len == 9;
                Color col;
                col.r = ParseValue(rgbvalue, 1);
                col.g = ParseValue(rgbvalue, 3);
                col.b = ParseValue(rgbvalue, 5);
                if (skin.useAlpha)
                    col.a = ParseValue(rgbvalue, 7);
                else
                    col.a = 1f;
                skin.color = col;
                return skin;
            }

            public override IUITweener CreateSkin(Object target)
            {
                var effect = target as Shadow;
                if (effect == null)
                    return null;
                return new EffectSkinColor(this, effect);
            }

            public void OnInitialize(Object target)
            {
                var effect = target as Shadow;
                if (effect == null)
                    return;
                var col = color;
                if (!useAlpha)
                    col.a = effect.effectColor.a;
                effect.effectColor = col;
            }
        }

        class EffectSkinColor : IUITweener
        {
            TweenEffectColor tween;
            Shadow effect;
            public EffectSkinColor(TweenEffectColor tween, Shadow target)
            {
                this.tween = tween;
                this.effect = target;
            }

            public void Activite(float duration)
            {
                if (effect != null)
                {
                    effect.effectColor = tween.color;
                }
            }

            public void Interrupt() { }
        }

        class TweenIntensity : UITweenState, IUIRecursiveState
        {
            public float FinalIntensity { get; private set; }
            public float Duration { get; private set; }

            public TweenIntensity() : base("intensity") { }
            public TweenIntensity(int id) : base(id) { }

            public override UITweenState Clone(string stateName, string content)
            {
                float v;
                if(float.TryParse(content, out v))
                {
                    var inst = new TweenIntensity(id);
                    inst.FinalIntensity = v;
                    return inst;
                }
                else
                {
                    return null;
                }
            }

            public override UITweenState Clone(string stateName, JsonData data)
            {
                var tp = data.GetJsonType();
                if (tp == JsonType.Double || tp == JsonType.Long)
                {
                    var inst = new TweenIntensity(id);
                    inst.FinalIntensity = (float)data;
                    inst.Duration = 0;
                    return inst;
                }
                else if (tp == JsonType.Object)
                {
                    var inst = new TweenIntensity(id);
                    inst.FinalIntensity = (float)data["intensity"];
                    inst.Duration = (float)data["duration"];
                    return inst;
                }
                else
                {
                    return Clone(stateName, (string)data);
                }
            }

            public override IUITweener CreateSkin(Object target)
            {
                return new EffectIntensity(this, (UIAdditionalData)target);
            }

            public override Object GetSkinComponent(GameObject gameObject)
            {
                return gameObject.GetComponent<UIAdditionalData>();
            }

            Object[] IUIRecursiveState.GetSkinComponents(GameObject root)
            {
                return root.GetComponentsInChildren<UIAdditionalData>();
            }
        }

        class EffectIntensity : IUITweener, IYieldCommand
        {
            TweenIntensity mIntensity;
            UIAdditionalData mAddData;
            ParallelDispatcher.Handler mJob;
            public EffectIntensity(TweenIntensity tween, UIAdditionalData addData)
            {
                mIntensity = tween;
                mAddData = addData;
            }

            public void Activite(float duration)
            {
                if(mIntensity.Duration >= 0.1f)
                {
                    mJob.Abort();
                    mJob = ParallelUtils.Schedule(this, true);
                }
                else
                {
                    mAddData.Intensity = mIntensity.FinalIntensity;
                }
            }

            public void Interrupt()
            {
                mJob.Abort();
            }

            IEnumerator IYieldCommand.Execute()
            {
                var speed = 1f / mIntensity.Duration;
                var v0 = mAddData.Intensity;
                var dur = Mathf.Abs(mIntensity.FinalIntensity - v0) / speed;
                var t = 0f;
                while (t < dur)
                {
                    yield return null;
                    t += Time.unscaledDeltaTime;
                    mAddData.Intensity = Mathf.Lerp(v0, mIntensity.FinalIntensity, Mathf.Clamp01(t / dur));
                }
            }
        }
    }
}